package me.kagami.fup;

import com.intellij.openapi.project.Project;
import com.intellij.psi.*;
import com.intellij.psi.search.GlobalSearchScope;
import com.intellij.psi.search.searches.ReferencesSearch;
import com.intellij.psi.util.PropertyUtil;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.util.Query;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

public class FindUsagesPlusHandler {
    private Project project;
    private PsiField psiField;

    private ResultCollector resultCollector;

    public FindUsagesPlusHandler(Project project) {
        this.project = project;
        this.resultCollector = new ResultCollector(project);
    }

    public void doHandle(PsiField psiField) {

        this.psiField = psiField;
        PsiMethod setterForField = PropertyUtil.findSetterForField(psiField);

        Query<PsiReference> search = ReferencesSearch.search(setterForField, GlobalSearchScope.projectScope(project), false);
        Collection<PsiReference> psiReferences = search.findAll();
        if (psiReferences.size() == 0) {
            //终点，说明没有赋值的地方
            System.out.println("没有赋值的地方：" + this.psiField.getName());
            resultCollector.collect(psiField, ResultCollector.NOT_ASSIGN, null);
            return;
        }
        if (psiReferences.size() > 5) {
            System.out.println("set调用处大于5处，跳过");
            resultCollector.collect(psiField, ResultCollector.BIGGER_5, null);
            return;
        }
        for (PsiReference psiReference : psiReferences) {
            //最外层需要new 循环监测的set，因为每一次处理Reference都是新的一轮判定，因为这个原因，所以不和递归用同样的方法。
            List<PsiMethod> cycleCollection = new ArrayList<>();
            cycleCollection.add(setterForField);
            System.out.println("开始处理引用：" + psiReference.getCanonicalText());
            PsiCall psiCall = PsiTreeUtil.getParentOfType(psiReference.getElement(), PsiCall.class);
            PsiExpressionList argumentList = psiCall.getArgumentList();
            if (argumentList == null || argumentList.getExpressions().length == 0) {
                System.out.println("set方法的入参为空，不统计这种情况 " + psiCall.getText());
                continue;
            }
            PsiExpression[] psiExpressions = argumentList.getExpressions();
            if (psiExpressions.length > 1) {
                System.out.println("不支持大于两个入参的Set方法" + psiCall.getText());
                continue;
            }
            PsiExpression psiExpression = psiExpressions[0];//b.getName()
            if (psiExpression instanceof PsiMethodCallExpression) {
                PsiMethodCallExpression psiMethodCallExpression = (PsiMethodCallExpression) psiExpression;
                PsiMethod psiMethod = psiMethodCallExpression.resolveMethod();
                //通过get方法获取set方法
                //这里约定，只能是a.setName(b.getName())这种形式，并且只有一个实现，不可重载
                PsiMethod[] setMethods = psiMethod.getContainingClass().findMethodsByName("s" + psiMethod.getName().substring(1), false);
                if (setMethods == null || setMethods.length == 0) {
                    //终点，如果b类没有set方法，那么说明找到终点
                    System.out.println(this.psiField.getName() + "  --> " + psiMethod.getContainingClass().getName() + " -- " + psiMethod.getName());
                    resultCollector.collect(psiField, psiMethod, cycleCollection, psiMethod);
                    continue;
                }
                //说明b是中间类，还有set方法需要分析
                handleInner(psiMethod, setMethods[0], cycleCollection);
            } else {
                //可能是a.setName("")这种形式
                System.out.println("找到一个终点" + psiCall.getText());

                resultCollector.collect(psiField, psiCall, cycleCollection, "基本类型赋值:" + psiCall.getArgumentList().getExpressions()[0].getText(), psiReference.getElement());
            }
        }

    }

    private void handleInner(PsiMethod getterForField, PsiMethod setterForField, List<PsiMethod> cycleCollection) {
        cycleCollection.add(setterForField);
        Query<PsiReference> search = ReferencesSearch.search(setterForField, GlobalSearchScope.projectScope(project), false);
        Collection<PsiReference> psiReferences = search.findAll();
        if (psiReferences.size() == 0) {
            //终点，说明b的set方法没有被调用过。那么说明b的值可能是查询出来的，这种情况应该被统计。
            System.out.println("找到一个终点" + this.psiField.getName() + "  -->" + setterForField.getContainingClass().getName() + " -- " + setterForField.getName());
            resultCollector.collect(psiField, getterForField, cycleCollection, getterForField);
            return;
        }
        if (psiReferences.size() > 5) {
            System.out.println("set调用处大于5处，跳过");
            resultCollector.collect(psiField, setterForField, cycleCollection, ResultCollector.BIGGER_5, null);
            return;
        }
        for (PsiReference psiReference : psiReferences) {
            System.out.println("开始处理引用：" + psiReference.getCanonicalText());
            PsiCall psiCall = PsiTreeUtil.getParentOfType(psiReference.getElement(), PsiCall.class);
            PsiExpressionList argumentList = psiCall.getArgumentList();
            if (argumentList == null || argumentList.getExpressions().length == 0) {
                System.out.println("set方法的入参为空，不统计这种情况 " + psiCall.getText());
                continue;
            }
            PsiExpression[] psiExpressions = argumentList.getExpressions();
            if (psiExpressions.length > 1) {
                System.out.println("不支持大于两个入参的方法" + psiCall.getText());
                continue;
            }
            PsiExpression psiExpression = psiExpressions[0];//b.getName()
            if (psiExpression instanceof PsiMethodCallExpression) {
                PsiMethodCallExpression psiMethodCallExpression = (PsiMethodCallExpression) psiExpression;
                PsiMethod psiMethod = psiMethodCallExpression.resolveMethod();
                //通过get方法获取set方法
                //这里约定，只能是a.setName(b.getName())这种形式，并且只有一个实现，不可重载
                PsiMethod[] setMethods = psiMethod.getContainingClass().findMethodsByName("s" + psiMethod.getName().substring(1), false);
                if (setMethods == null || setMethods.length == 0) {
                    //终点，如果b类没有set方法，那么说明找到终点
                    System.out.println(this.psiField.getName() + "  --> " + psiMethod.getContainingClass().getName() + " -- " + psiMethod.getName());
                    resultCollector.collect(psiField, psiMethod, cycleCollection, psiMethod);
                    continue;
                }
                //循环监测，可能存在a.setName(b.getName) b.setName(a.setName)的情况。
                if (!cycleCollection.contains(setMethods[0])) {
                    handleInner(psiMethod, setMethods[0], cycleCollection);
                } else {
                    System.out.println("Set方法循环,跳过");
                    continue;
                }
            } else {
                //可能是b.setName("")这种形式
                System.out.println("找到一个终点" + psiCall.getText());
                resultCollector.collect(psiField, psiCall, cycleCollection, "基本类型赋值" + psiCall.getArgumentList().getExpressions()[0].getText(), psiReference.getElement());
            }
        }
    }


    public ResultCollector getResultCollector() {
        return resultCollector;
    }
}
